﻿using System;

namespace LightCAD.Core
{
    public struct Box2d
    {
        /// <summary>
        /// 长宽为0的矩形
        /// </summary>
        public static readonly Box2d Zero = new Box2d
        {
            Location = default,
            Width = 0.0,
            Height = 0.0
        };

        /// <summary>
        /// 空白(无效的)矩形
        /// </summary>
        public static readonly Box2d Empty = new Box2d()
        {
            Location = new Vector2d(),
            Width = -1,
            Height = -1,
        };


        /// <summary>
        /// 位置,也就是矩形的左下角坐标
        /// </summary>
        public Vector2d Location;

        /// <summary>
        /// 宽、高
        /// </summary>
        public double Width;
        public double Height;

        public Vector2d LeftBottom
        {
            get
            {
                return Location;
            }
        }

        public Vector2d LeftTop
        {
            get
            {
                return new Vector2d(this.Location.X, this.Location.Y + Height);
            }
        }

        public Vector2d RightTop
        {
            get
            {
                return new Vector2d(this.Location.X + this.Width,
                    this.Location.Y + this.Height);
            }
        }

        public Vector2d RightBottom
        {
            get
            {
                return new Vector2d(this.Location.X + this.Width, this.Location.Y);
            }
        }
        public Vector2d Center
        {
            get
            {
                return new Vector2d(this.Location.X + this.Width / 2, this.Location.Y + this.Height / 2);
            }
        }
        public Box2d(double x, double y, double width, double height)
        {
            this.Location = new Vector2d(x,y);
            this.Width = width;
            this.Height = height;
        }
        public Box2d(Vector2d location, double width, double height)
        {
            this.Location = location;
            this.Width = width;
            this.Height = height;
        }

        public Box2d(Vector2d point1, Vector2d point2)
        {
            double minX = point1.X < point2.X ? point1.X : point2.X;
            double minY = point1.Y < point2.Y ? point1.Y : point2.Y;
            this.Location = new Vector2d(minX, minY);
            this.Width = Math.Abs(point2.X  - point1.X);
            this.Height = Math.Abs(point2.Y - point1.Y);
        }

        public bool IntersectWith(Box2d box)
        {
            var boxMax = box.RightTop;
            var thisMax = this.RightTop;

            // using 4 splitting planes to rule out intersections
            return boxMax.X          < this.Location.X
                   || box.Location.X > thisMax.X
                   || boxMax.Y       < this.Location.Y
                   || box.Location.Y > thisMax.Y ? false : true;
        }

        public Box2d Inflate(int width, int height)
        {
            Vector2d min = new Vector2d();
            min.X = this.LeftBottom.X - width / 2;
            min.Y = this.LeftBottom.Y - height / 2;
            Vector2d max = new Vector2d();
            min.X = this.RightTop.X + width / 2;
            min.Y = this.RightTop.Y + height / 2;
            return new Box2d(min, max);
        }

        /// <summary>
        /// Check whether contains point
        /// </summary>
        public bool Contains(Box2d box)
        {
            return this.Contains(box.Location) && this.Contains(box.RightTop);
        }
        /// <summary>
        /// Check whether contains point
        /// </summary>
        public bool Contains(Vector2d point)
        {
            return this.Contains(point.X, point.Y);
        }

        /// <summary>
        /// Check whether contains point: (x, y)
        /// </summary>
        public bool Contains(double x, double y)
        {
            return x    >= this.Location.X
                   && x <= this.Location.X + this.Width
                   && y >= this.Location.Y
                   && y <= this.Location.Y + this.Height;
        }

        public override string ToString()
        {
            return string.Format("Box2d( {0}, {1}), {2}, {3} )", this.Location.X, this.Location.Y, this.Width, this.Height);
        }

        public void Union(Box2d box)
        {
            if (this.Width < 0 || this.Height < 0)
            {
                this.Location = box.Location;
                this.Width = box.Width;
                this.Height = box.Height;
            }
            else
            {
                double trX = Math.Max(this.Location.X + this.Width, box.Location.X + box.Width);
                double trY = Math.Max(this.Location.Y + this.Height, box.Location.Y + box.Height);
                this.Location.X = Math.Min(this.Location.X, box.Location.X);
                this.Location.Y = Math.Min(this.Location.Y, box.Location.Y);

                this.Width = trX - this.Location.X;
                this.Height = trY - this.Location.Y;
            }

        }

        public Box2d SetEmpty()
        {
            this.Location = new Vector2d();
            this.Width = -1;
            this.Height = -1;
            return this;
        }

        public Box2d ExpandByPoints(params Vector2d[] points)
        {
            Vector2d min = new Vector2d(double.MaxValue, double.MaxValue);
            Vector2d max = new Vector2d(double.MinValue, double.MinValue);
            foreach (Vector2d p in points)
            {
                min.X = Math.Min(min.X, p.X);
                min.Y = Math.Min(min.Y, p.Y);
                max.X = Math.Max(max.X, p.X);
                max.Y = Math.Max(max.Y, p.Y);
            }
            this.Union(new Box2d(min, max));
            return this;
        }

        public Rect2d ToRect()
        {
            return new Rect2d { Min = LeftBottom, Max = RightTop };
        }

        public Polygon2d ToPolygon()
        {
            //逆时针方向
            return new Polygon2d()
            {
                Points = new Vector2d[] {
                    LeftBottom,RightBottom,RightTop,LeftTop
                }
            };
        }
        public Polygon2d ToPolygon(Matrix3d matrix)
        {
            //逆时针方向
            return new Polygon2d()
            {
                Points = new Vector2d[] {
                    matrix.MultiplyPoint(LeftBottom),
                    matrix.MultiplyPoint(RightBottom),
                    matrix.MultiplyPoint(RightTop),
                    matrix.MultiplyPoint(LeftTop)
                }
            };
        }

    }
}
